#include <std.h> //Standard
#include <error.h>
#include <type.h>
#include <memory.h>
#include <irq.h>
#include <text.h>
#include <shell.h>

/*********************************Kernel function file***************************/

#define TBL_SZ 0x1000

static unsigned long mem_table[TBL_SZ];
static unsigned char flag_table[TBL_SZ];
static u32 tag_table[TBL_SZ];

static spin_lock_t mm_lock;

static int last_alloc=0; 

extern unsigned long _end;

void *search_mem(unsigned char *start_pointer,unsigned char search_char,unsigned char *end_pointer)
{
	unsigned char *search;
	for(search=start_pointer;search<end_pointer;search++)	{
		if(*search==search_char)
			return search;
	}
	return NULL;
}

int swapmem(char *m1,char *m2,size_t length)
{
	char *temp=NULL;
	while(--length>0)	{
		*temp=*m1;
		*m1=*m2;
		*m2=*temp;
	}
	return NULL;
}

int swap32mem(unsigned long *m1,unsigned long *m2,size_t length)
{
	unsigned long *temp=NULL;
	while(--length>0)	{
		*temp=*m1;
		*m1=*m2;
		*m2=*temp;
	}
	return NULL;
}
	

int shift_table_down(int start)
{
	int i;
	for(i=TBL_SZ-1;i>start;i--)	{
		mem_table[i]=mem_table[i-1];
		flag_table[i]=flag_table[i-1];
	}
	mem_table[start]=NULL;
	return NULL;
}

int shift_table_up(int start)
{
	int i;
	for(i=start;i<TBL_SZ-1;i++)	{
		mem_table[i]=mem_table[i+1];
		flag_table[i]=flag_table[i+1];
	}
	return NULL;
}

char *memnull="Memory request return is null!";

static unsigned show_mm=0,zero_memory=0;

void *get_mem(size_t b)
{
	int i;
	acquire_spin_lock(&mm_lock);
	for(i=0;i<TBL_SZ;i++)	{
		if(is_allocated(i)==NULL)	{
			if(allocation_size(i)>b)	{
				shift_table_down(i+1);
				make_entry(i+1,mem_table[i]+b);
				set_allocated(i);
				release_spin_lock(&mm_lock);
				sane(!mem_table[i],memnull);
				last_alloc=i;
				if(zero_memory)
					__memset((void *)mem_table[i],0,b);
				if(show_mm)
					printk("<Memory:%x,%d>",mem_table[i],b);
				return (void *)mem_table[i];
			}
			else if(allocation_size(i)==b)	{
				set_allocated(i);
				release_spin_lock(&mm_lock);
				sane(!mem_table[i],memnull);
				last_alloc=i;
				if(zero_memory)
					__memset((void *)mem_table[i],0,b);
				if(show_mm)
					printk("<Memory:%x,%d>",mem_table[i],b);
				return (void *)mem_table[i];
			}
		}
	}
	release_spin_lock(&mm_lock);
	return NULL;
}

void *get_mem_tag(size_t b,u32 tag)
{
	int i;
	acquire_spin_lock(&mm_lock);
	for(i=0;i<TBL_SZ;i++)	{
		if(is_allocated(i)==NULL)	{
			if(allocation_size(i)>b)	{
				shift_table_down(i+1);
				make_entry(i+1,mem_table[i]+b);
				set_allocated(i);
				tag_table[i]=tag;
				sane(!mem_table[i],memnull);
				release_spin_lock(&mm_lock);
				return (void *)mem_table[i];
			}
			else if(allocation_size(i)==b)	{
				set_allocated(i);
				tag_table[i]=tag;
				sane(!mem_table[i],memnull);
				release_spin_lock(&mm_lock);
				return (void *)mem_table[i];
			}
		}
	}
	release_spin_lock(&mm_lock);
	return NULL;
}


void *get_tag(u32 tag)
{
	int i;
	for(i=0;i<TBL_SZ;i++)	{
		if(tag_table[i]==tag)	{
			if(is_allocated(i))
				return (void *)mem_table[i];
		}
	}
	return NULL;
}

int search_allocation(void *p)
{
	int i;
	for(i=0;i<TBL_SZ;i++)	{
		if((mem_table[i])==(unsigned long)p)
			return i;
	}
	stop("MEMORY FAILED Invalid pointer:%x",p);
	return NULL;
}

int merge_free()
{
	int i,merges=0;
	for(i=0;i<TBL_SZ-1;i++)
	{
		if(is_allocated(i)==NULL && is_allocated(i+1)==NULL && ((mem_table[i+1])!=0xffffffff))	{
			shift_table_up(i+1);
			merges++;
		}
	}
	return merges;
}

int free(void *p)
{
	int i;
	acquire_spin_lock(&mm_lock);
	sane(!p,"memfree");
	i=search_allocation(p);
	set_free(i);
	tag_table[i]=0xffffffff;
	i=1;
	while(i)	{
		i=merge_free();
	}
	if(show_mm)
		printk("<Mfree:%x>",p);
	release_spin_lock(&mm_lock);
	return NULL;
}

int reallocate(void *old,size_t b)
{
	int i;
	size_t size;
	void *temp;
	acquire_spin_lock(&mm_lock);
	sane(!old,"Memory realloc:invalid pointer");
	temp=old;
	i=search_allocation(old);
	size=allocation_size(i);
	free(old);
	old=get_mem(b);
	memcpy(old,temp,size);
	release_spin_lock(&mm_lock);
	return NULL;
}

size_t avg_allocation_size()
{
	int i=0;
	size_t avg=0;
	while(allocation_size(i)!=NULL)	{
		avg+=allocation_size(i);
		i++;
	}
	avg/=TBL_SZ;
	return avg;
}

unsigned long mem_occ()
{
	int i;
	unsigned long t=0;
	for(i=1;i<TBL_SZ;i++)	{
		if(mem_table[i]==0xffffffff)
			break;
		if(is_allocated(i-1))
			t+=allocation_size(i-1);
	}
	return t;
}

unsigned long sys_occ()
{
	return (_end-0x100000);
}

int hash_location(void *p)
{
	int temp;
	temp=(int)(p-mem_table[0]);
	temp/=avg_allocation_size();
	return temp;
}

int up_mm()
{
	if(mem_table[0]!=0xffffffff)
		return FAILED;
	return TRUE;
}



void memspd(u32 alloc)
{
	u32 *m1,*m2;
	u32 tsc_lo,tsc_hi,tscf_lo,tscf_hi;
	u32 ms;
	printk("\nMemory speed with memcpy");
	m1=(u32 *)get_mem(alloc/2);
	m2=(u32 *)get_mem(alloc/2);
	kernel_lock();	/*Absolute silence*/
	read_tsc(&tsc_lo,&tsc_hi);
	if(alloc/2)
		_memcpy_mmx(m1,m2,alloc/2);
	read_tsc(&tscf_lo,&tscf_hi);
	kernel_unlock();
	printk("\ntsc start:%d",tsc_lo);
	printk("\ntsc:%d %d",tscf_hi-tsc_hi,tscf_lo-tsc_lo);
	ms=(tscf_lo-tsc_lo)/cpu_ms();
	printk("\ntime:%d ms",ms);
	if(ms)
		printk("\nMemory speed:%d MB/s",(alloc/1000)/(ms));
	free(m1);
	free(m2);
}

int cmd_memspd(struct param_list *params)
{
	memspd(params->integer);
	return NULL;
}



void init_mm2()
{
	create_setting_val("show_mm",&show_mm);
	create_setting_val("zero_memory",&zero_memory);
	register_cmd("memspd",cmd_memspd,1,"Calculates memory speed. Pass the amount of memory to read in bytes");
}

void show_memory_table()
{
	int i;
	printk("\nMemory table:");
	for(i=0;i<40;i++) {
		printk("[%x:%c]",mem_table[i],is_allocated(i)?'O':'F');
	}
}

void init_mm()
{
	int i;
	for(i=0;i<TBL_SZ;i++)	{
		mem_table[i]=0xffffffff;
		flag_table[i]=NULL;
		tag_table[i]=0xffffffff;
	}
	make_entry(0,_end);
}
